package com.jingdianjichi.auth.domain.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.google.gson.Gson;
import com.jingdianjichi.auth.common.enums.AuthUserStatusEnum;
import com.jingdianjichi.auth.common.enums.IsDeletedFlagEnum;
import com.jingdianjichi.auth.domain.constants.AuthConstant;
import com.jingdianjichi.auth.domain.convert.AuthBOConverter;
import com.jingdianjichi.auth.domain.entity.AuthUserBO;
import com.jingdianjichi.auth.domain.redis.RedisUtil;
import com.jingdianjichi.auth.domain.service.AuthUserDomainService;
import com.jingdianjichi.auth.infra.basic.entity.*;
import com.jingdianjichi.auth.infra.basic.service.*;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class AuthUserDomainServiceImpl implements AuthUserDomainService {

    @Resource
    private AuthUserService authUserService;
    @Resource
    private AuthRoleService authRoleService;
    @Resource
    private AuthUserRoleService authUserRoleService;
    @Resource
    private AuthRolePermissionService authRolePermissionService;
    @Resource
    private AuthPermissionService authPermissionService;

    //用户密码加盐
    private String salt = "hao";

    //
    private static final String LOGIN_PREFIX = "loginCode";
    @Resource
    private RedisUtil redisUtil;

    private String authPermissionPrefix = "auth.permission";

    private String authRolePrefix = "auth.role";

    /**
     * 用户注册
     */
    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(AuthUserBO authUserBO) {

        //校验用户是否存在
        AuthUser existAuthUser = new AuthUser();
        existAuthUser.setUserName(authUserBO.getUserName());
        List<AuthUser> existUser = authUserService.queryByCondition(existAuthUser);
        if (existUser.size() > 0) {
            return true;
        }
        AuthUser authUser = AuthBOConverter.INSTANCE.convertAuthUserBOToEntity(authUserBO);
        //用户密码加盐使用sa框架加密处理
        if (StringUtils.isNotBlank(authUser.getPassword())) {
            authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(), salt));
        }

        //设置用户状态为启用
        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        //插入数据库用户基本信息
        Integer count = authUserService.insert(authUser);

        //建立用户角色关联
        //设置角色为普通用户
        AuthRole authRole = new AuthRole();
        authRole.setRoleKey(AuthConstant.NORMAL_USER);
        //查询角色为普通用户的角色id
        AuthRole roleResult = authRoleService.queryByCondition(authRole);
        //建立用户和角色id关系，插入用户角色表
        AuthUserRole authUserRole = new AuthUserRole();
        authUserRole.setUserId(authUser.getId());
        authUserRole.setRoleId(roleResult.getId());
        authUserRole.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        authUserRoleService.insert(authUserRole);

        //存入redis用户角色
        //构造redis用户角色键
        String roleKey = redisUtil.buildKey(authRolePrefix, authUser.getUserName());
        //构造redis值
        LinkedList<AuthRole> roleList = new LinkedList<>();
        roleList.add(authRole);
        //使用gson反序列化把键和值存入redis中
        redisUtil.set(roleKey, new Gson().toJson(roleList));

        //存入redis用户权限
        //通过角色id查询角色对应的权限集合
        AuthRolePermission authRolePermission = new AuthRolePermission();
        authRolePermission.setRoleId(roleResult.getId());
        List<AuthRolePermission> rolePermissionList = authRolePermissionService.queryByCondition(authRolePermission);
        //使用stream流取出权限id存入集合
        List<Long> roleIdList = rolePermissionList.stream().map(AuthRolePermission::getPermissionId).collect(Collectors.toList());

        //根据权限Id集合去权限表中查询对应的权限，为redis用户权限值
        List<AuthPermission> permissionList = authPermissionService.queryByRoleListId(roleIdList);

        //构建redis用户权限键
        String permissionKey = redisUtil.buildKey(authPermissionPrefix, authUser.getUserName());
        //使用gson反序列化把键和值存入redis中
        redisUtil.set(permissionKey, new Gson().toJson(permissionList));

        return count > 0;
    }

    /**
     * 修改用户信息
     */
    @Override
    public Boolean update(AuthUserBO authUserBO) {
        AuthUser authUser = AuthBOConverter.INSTANCE.convertAuthUserBOToEntity(authUserBO);
        Integer count = authUserService.updateByUserName(authUser);

        return count > 0;
    }

    @Override
    public Boolean delete(AuthUserBO authUserBO) {
        AuthUser authUser = new AuthUser();
        authUser.setId(authUserBO.getId());
        authUser.setIsDeleted(IsDeletedFlagEnum.DELETED.getCode());
        Integer count = authUserService.update(authUser);
        return count > 0;
    }

    @Override
    public Boolean changeStatus(AuthUserBO authUserBO) {
        AuthUser authUser = new AuthUser();
        authUser.setId(authUserBO.getId());
        authUser.setStatus(authUserBO.getStatus());
        Integer count = authUserService.update(authUser);
        return count > 0;
    }

    /**
     * 用户登录携带验证码
     * @param validCode
     * @return
     */
    @Override
    public SaTokenInfo doLogin(String validCode) {
        //构建loginCode+validCode键
        String loginKey = redisUtil.buildKey(LOGIN_PREFIX, validCode);
        //通过键取值
        String openId = redisUtil.get(loginKey);
        //如果openId为空则返回null,登录失败
        if (StringUtils.isBlank(openId)) {
            return null;
        }
        //注册用户信息，openId作为用户名
        AuthUserBO authUserBO = new AuthUserBO();
        authUserBO.setUserName(openId);
        register(authUserBO);

        //Sa-Token会在服务器端和客户端（如浏览器的Cookie中）记录这个用户的登录状态
        // 以便在用户后续请求时能够识别出用户的身份。
        StpUtil.login(openId);
        //返回一个SaTokenInfo对象，这个对象包含了当前Token的详细信息
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        return tokenInfo;
    }

    @Override
    public AuthUserBO getUserInfo(AuthUserBO authUserBO) {
        AuthUser authUser = AuthBOConverter.INSTANCE.convertAuthUserBOToEntity(authUserBO);
        List<AuthUser> authUserList = authUserService.queryByCondition(authUser);
        if(CollectionUtils.isEmpty(authUserList)){
            return new AuthUserBO();
        }
        return AuthBOConverter.INSTANCE.convertAuthUserToBO(authUserList.get(0));
    }
}
